package com.cheetah.id.generator;

import com.cheetah.exceptions.CheetahRuntimeException;
import com.cheetah.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Administrator
 * @Date 2020/7/1
 * @Version V1.0
 * @Description: 单机id生成器，可以做成主备模式，重启会出现断号，但id生产为单调递增
 **/
@Component
public class Generator {

    @Autowired
    private RedisUtil redisUtil;

    private volatile static long m = 0L;

    private Map<String,Long> maxMap = new HashMap<>();//最大缓存值集合

    private Map<String,Long> idMap = new HashMap<>(); //id集合


    /**
     * id生产
     * @param workId 业务id
     * @param step 步长
     * @return
     */
    public synchronized long nextId(String workId,long step){

        if(step <= 0){
            throw new CheetahRuntimeException("步长id必须为正整数！");
        }

        if(idMap.get(workId) == null){
            idMap.put(workId,0L);
            maxMap.put(workId,step);
            redisUtil.hset("h:seq:max",workId,step); //超过最大值后持久化
            redisUtil.hset("h:seq:step",workId,step);
            return 0L;
        }else{
            long id = idMap.get(workId);
            long max = maxMap.get(workId);
            if(id < max){
                id ++;
                idMap.put(workId,id);
                return id;
            }
            max += step;
            maxMap.put(workId,max);
            redisUtil.hset("h:seq:max",workId,max); //超过最大值后持久化
            redisUtil.hset("h:seq:step",workId,step);
            return id ++;
        }

    }

    //应用启动后初始化初始值
    @PostConstruct
    public void init(){

        Map<String,Long> map = redisUtil.hmget("h:seq:max");

        if(map != null){
            map.forEach( (k,v) -> {
                long max = (Long)redisUtil.hget("h:seq:max",k);
                long step = (Long)(redisUtil.hget("h:seq:step",k));

                idMap.put(k,max);
                maxMap.put(k,max + step);
                redisUtil.hset("h:seq:max",k,max + step);
            });
        }

    }
}
